/*
 * Distribuidor.java
 *
 * Copyright 2011 Universidad Francisco de Paula Santander (UFPS).
 * Todos los Derechos Reservados.
 * Creado el 12/12/2009, 11:23:38 AM
 */
package com.ufps.cuatro.modelo.representacion.componente;

import com.ufps.cuatro.controlador.render.UtilRender;
import java.awt.GridBagConstraints;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.ufps.cuatro.modelo.representacion.PlantillaCombo;
import com.ufps.cuatro.util.Utileria;
import com.ufps.cuatro.util.base.ObjectBase;

/**
 * <i><b>Distribuidor:</b></i><p>Esta Clase provee los mecanismos necesarios para
 * distribuir los <code>Componente</code>s Contenidos en una Representación.</p>
 * 
 * @param <T> Referencia al <code>ComponenteDistribuidor</code> Padre.
 * @author Carlos A. Torres C. - Dolly E. Guerrero. <https://code.google.com/p/tesis4dor/>
 * @version 1.0 12/12/2009
 * @see ObjectBase
 */
public class Distribuidor extends ObjectBase{
    private static final long serialVersionUID = 5701783065874406974L; 
    
    /**
     * Valor Inicial por Defecto el Tamaño Máximo de la tabla de Distribución.
     */
    public final static int MAX_SIZE_GRID_INIT = 8;
    
    /**
     * Almacena los Titulos de la Tabla que permite visualizar sus componentes incluidos
     * (<code>ComponenteGeneradoSimple</code>).
     */
    protected String[] objTituloTablaComponente = {"Id", "Componente", "Tipo"};
    /**
     * Contiene las Referencias de los Componentes inluidos solo a nivel informativo
     * para visualizaciones de Contenido.
     */
    protected Object[][] objDataTablaComponente;    
    
    /**
     * Almacena los Titulos ó numeros de columna en los cuales se distribuirá el Componente
     * y sus componentes incluidos (<code>ComponenteGeneradoSimple</code>) en el Contenedor de
     * alto nivel.
     */    
    protected String[] objTituloTablaDistribucion;    
    /**
     * Contiene las Referencias del Componente y sus Componentes incluidos 
     * (<code>ComponenteGeneradoSimple</code>) distribuidos en un plano matricial (<code>JTable</code>)
     * que simula la dsitribución en un Contenedor de Alto nivel.
     */
    protected Object[][] objDataTablaDistribucion;       
    /**
     * Contiene la Coleccion de Componentes Incluidos generada por
     * distribución automática.
     */
    protected ArrayList<Componente> objComponentesDistribuidos;     
    /**
     * Contiene la <code>PlantillaCombo</code> de la Distribucion. 
     */
    protected PlantillaCombo objPlantillaCombo;

    /**
     * Constructor que Inicializa la Propiedades para la Distribución representada.
     * @param objComponenteParent <code>ComponenteDistribuidor</code> que Genera la Distribución.
     * @param objComponentesDistribuidos Colección de <code>Componente</code> Incluidos en la Distribución.
     * @param objPlantillaCombo <code>PlantillaCombo</code> que Representa la Distribución.
     */
    public Distribuidor(ArrayList<Componente> objComponentesDistribuidos, PlantillaCombo objPlantillaCombo) {        
        this.objComponentesDistribuidos = objComponentesDistribuidos;
        this.objPlantillaCombo = objPlantillaCombo;
    }
    
    /**
     * Genera la Distribución de los <code>Componente</code> Incluidos 
     * en una Matriz de <code>Object</code> que luego crearán el Modelo para
     * la Tabla donde se Visualizará la Representación.
     * @return boolean que determina el resultado del proceso de distribución.
     */    
    public boolean generarTablaDistribucion(){        
//        try{
        if(objComponentesDistribuidos != null){
            if(!this.objComponentesDistribuidos.isEmpty()){
                return distribuirPorComponentesIncluidos();
            } 
            else {
                return distribuirPorDefecto();
            }
//        }catch(java.lang.Exception ex){
//            Logger.getLogger(Distribuidor.class.getName()).log(Level.SEVERE, null, ex);
//        }
        }else{
            UtilRender.printlnConsole("Componentes Distribuidos Nulo.", UtilRender.ACTION_NORMAL, false);            
        }
        return false;
    }

    private boolean distribuirPorDefecto() {
        int filas = MAX_SIZE_GRID_INIT;
        int columnas = MAX_SIZE_GRID_INIT;
        Object[][] objTempDataTablaDistribucion = new Object[filas][columnas];
        this.objTituloTablaDistribucion = new String[objTempDataTablaDistribucion[0].length];
        for(int i = 0; i < objTempDataTablaDistribucion[0].length; i++){
            this.objTituloTablaDistribucion[i] = "Posición " + i;
        }
        for(int i = 0; i < filas; i++){
            for(int j = 0; j < columnas; j++){
                objTempDataTablaDistribucion[i][j] = "";
            }
        }           
        this.objDataTablaDistribucion = objTempDataTablaDistribucion;                
        return true;
    }

    private boolean distribuirPorComponentesIncluidos() {
        actualizarWeightXY();
        int filas = this.getSizeHeightColection();
        int columnas = this.getSizeWidthColection();
        
        this.objDataTablaComponente = null;
        this.objDataTablaDistribucion = null;
        this.objTituloTablaComponente = null;
        this.objTituloTablaDistribucion = null;
        
        Object[][] objTempDataTablaDistribucion = new Object[filas][columnas];
        this.objTituloTablaDistribucion = new String[objTempDataTablaDistribucion[0].length];
        for(int i = 0; i < objTempDataTablaDistribucion[0].length; i++){
            this.objTituloTablaDistribucion[i] = "Posición " + i;
        }
        int ubicados = 0;
        for(Componente objComponente : this.objComponentesDistribuidos){
            if(objComponente.ubicarWithGridBagConstraints(objTempDataTablaDistribucion, false) == Componente.UBICACION_REALIZADA){
                System.out.println("Componente: " + objComponente.getName() + " incluido.");
                ubicados++;
            }
        }
        //Los Componentes pueden estar asignados pero no se han distribuido
        if(ubicados == 0){            
            for(Componente objComponente : this.objComponentesDistribuidos){
                objComponente.getObjPlantillaComponenteDistribucionIndividualSeleccionada().setIncluido(false);
                if(objComponente.ubicarWithGridBagConstraints(objTempDataTablaDistribucion, true) == Componente.UBICACION_REALIZADA){
                    System.out.println("Componente: " + objComponente.getName() + " incluido.");
                    ubicados++;
                }
            }            
        }
        if(ubicados > 0) this.objDataTablaDistribucion = objTempDataTablaDistribucion;                
        return true;
    }
    
    private void actualizarWeightXY(){
        int countComponentes = objComponentesDistribuidos.size();//this.recalcularWeightXY(objComponentesDistribuidos);
        if(countComponentes >= objComponentesDistribuidos.size() && countComponentes > 0){
            //Este valor debe ser mayor
            double porcentaje = 100 / countComponentes;
            double weight = porcentaje / 100;
            actualizarWeightToGridBagConstraints(objComponentesDistribuidos, weight);
        }
    }
    
    private void actualizarWeightToGridBagConstraints(ArrayList<Componente> objComponentesIncluidos, double weight){
        if(!objComponentesIncluidos.isEmpty()){
            double weightIncluido = weight / objComponentesIncluidos.size();
            for(Componente objComponente : objComponentesIncluidos){
                try{
                    if(objComponente instanceof ComponenteDistribuidor){
                        ComponenteDistribuidor objComponenteDistribuidor = (ComponenteDistribuidor) objComponente;
                        actualizarWeightToGridBagConstraints(objComponenteDistribuidor.getComponentesDistribuidos(), weightIncluido);
                    }
                    else if(objComponente instanceof ComponenteGeneradoSimple ||
                            objComponente instanceof ComponenteGeneradoInterno){                        
                        Utileria.viewConsole("Componente: " + objComponente.getName() + " actualizado weight(x,y)");
                    }
                }catch(java.lang.Exception ex){
                    Utileria.viewConsole("Componente: " + objComponente.getName() + " Error.");
                    Logger.getLogger(Distribuidor.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }
    
    private int recalcularWeightXY(ArrayList<Componente> objComponentesIncluidos){
        //Obtener la Cantidad de Controles en la Distribucion teniendo
        //en cuenta los Controles internos
        int countComponentes = 0;
        for(Componente objComponente : objComponentesIncluidos){
            if(objComponente instanceof ComponenteDistribuidor){
                ComponenteDistribuidor objComponenteDistribuidor = (ComponenteDistribuidor) objComponente;
                countComponentes += recalcularWeightXY(objComponenteDistribuidor.getComponentesDistribuidos());
            }
            else if(objComponente instanceof ComponenteGeneradoSimple ||
                    objComponente instanceof ComponenteGeneradoInterno){
                countComponentes++;
            }
        }
        return countComponentes;
    }
    
    /**
     * Devuelve el Máximo Limite posible de distribución Horizontal para el
     * <code>ComponenteDistribuidor</code>.
     * @return Máximo Limite posible de distribución Horizontal para el
     * <code>ComponenteDistribuidor</code>.
     */
    public int getSizeWidthColection(){
       if(objComponentesDistribuidos != null){
        int sizeWitdh = 0;        
        try{
            //Se busca los Valores Máximos para x y width
            for(Componente objComponenteIncluido : this.objComponentesDistribuidos){
                GridBagConstraints objGridBagConstraints = objComponenteIncluido.getGridBagConstraintsActiva();
                //GridBagConstraints objGridBagConstraints = objComponenteIncluido.getGridBagConstraintsActivaNormalizada();
                if(objGridBagConstraints != null){
                    sizeWitdh += objGridBagConstraints.gridwidth;                    
                }            
                else{
                    sizeWitdh += objComponenteIncluido.getObjGridBagConstraints().gridwidth;
                }                
            }           
        }catch(java.lang.Exception ex){            
            Logger.getLogger(Distribuidor.class.getName()).log(Level.SEVERE, null, ex);
        }
        return sizeWitdh;        
    } else{
            UtilRender.printlnConsole("Componnetes Distribuidos Nulos.", UtilRender.ACTION_NORMAL, false);            
        }
       return 0;
    }
    
    /**
     * Devuelve Máximo Limite posible de distribución Vertical para el
     * <code>ComponenteDistribuidor</code>.
     * @return Máximo Limite posible de distribución Vertical para el
     * <code>ComponenteDistribuidor</code>.
     */
    public int getSizeHeightColection(){
      if(objComponentesDistribuidos != null){
        int sizeHeight = 0;        
        try{            
            for(Componente objComponenteIncluido : this.objComponentesDistribuidos){
                GridBagConstraints objGridBagConstraints = objComponenteIncluido.getGridBagConstraintsActiva();
                //GridBagConstraints objGridBagConstraints = objComponenteIncluido.getGridBagConstraintsActivaNormalizada();
                if(objGridBagConstraints != null){
                    sizeHeight += objGridBagConstraints.gridheight;
                }            
                else{
                    sizeHeight += objComponenteIncluido.getObjGridBagConstraints().gridheight;
                }
            }           
        }catch(java.lang.Exception ex){            
            Logger.getLogger(Distribuidor.class.getName()).log(Level.SEVERE, null, ex);
        }
        return sizeHeight;        
    } else{
            UtilRender.printlnConsole("Componnetes Distribuidos Nulos.", UtilRender.ACTION_NORMAL, false);            
        }
      return 0;
    }

    /**
     * Devuelve la Referencia de la Colección de <code>Componente</code> Incluidos
     * en la Distribución.
     * @return Referencia de la Colección de <code>Componente</code> Incluidos
     * en la Distribución.
     */
    public ArrayList<Componente> getObjComponentesDistribuidos() {
       try{
        return objComponentesDistribuidos;
        }catch(java.lang.NullPointerException ex){}
       return null;
    }

    /**
     * Devuelve la Referencia de la Matriz de <code>Componente</code> Distribuidos.
     * @return Referencia de la Matriz de <code>Componente</code> Distribuidos.
     */
    public Object[][] getObjDataTablaDistribucion() {
        try{
        return objDataTablaDistribucion;
    }catch(java.lang.NullPointerException ex){}
        return null;
    }

    /**
     * Devuelve la Referencia del Array que Representa los Titulos de la Matriz
     * de <code>Componente</code> Distribuidos.
     * @return Referencia del Array que Representa los Titulos de la Matriz
     * de <code>Componente</code> Distribuidos.
     */
    public String[] getObjTituloTablaDistribucion() {
        try{
        return objTituloTablaDistribucion;
        }catch(java.lang.NullPointerException ex){}
        return null;
    }    

    /**
     * Establece la Referencia de la Matriz de <code>Componente</code> Distribuidos.
     * @param objDataTablaDistribucion Referencia de la Matriz de <code>Componente</code> Distribuidos.
     */
    public void setObjDataTablaDistribucion(Object[][] objDataTablaDistribucion) {
        this.objDataTablaDistribucion = objDataTablaDistribucion;
    }

    /**
     * Establece la Referencia del Array que Representa los Titulos de la Matriz
     * de <code>Componente</code> Distribuidos.
     * @param objTituloTablaDistribucion Referencia del Array que Representa los Titulos de la Matriz
     * de <code>Componente</code> Distribuidos.
     */
    public void setObjTituloTablaDistribucion(String[] objTituloTablaDistribucion) {
        this.objTituloTablaDistribucion = objTituloTablaDistribucion;
    }    
    
    /**
     * Agrega un <code>Componente</code> a la Colección de <code>Componente</code> Incluidos.
     * @param objComponente <code>Componente</code> que se incluirá en la Colección de <code>Componente</code> Incluidos.
     * @return boolean que determina si la inclusión se llevo a cabo correctamente.
     */
    public boolean agregarComponente(Componente objComponente){       
    try{
        if(this.objComponentesDistribuidos.contains(objComponente)){
            int index = this.objComponentesDistribuidos.indexOf(objComponente);
            this.objComponentesDistribuidos.set(index, objComponente);
            return true;
        }
        else{
            return this.objComponentesDistribuidos.add(objComponente);
        }
    }catch(java.lang.NullPointerException ex){}
    return false;
    }
    
    /**
     * Elimina un <code>Componente</code> a la Colección de <code>Componente</code> Incluidos.
     * @param objComponente <code>Componente</code> que se eliminará en la Colección de <code>Componente</code> Incluidos.
     * @return boolean que determina si la eliminación se llevo a cabo correctamente.
     */
    public boolean eliminarComponente(Componente objComponente){
      try{
        if(objComponentesDistribuidos.contains(objComponente)){            
            return objComponentesDistribuidos.remove(objComponente);
        }
      }catch(java.lang.NullPointerException ex){}
        return false;
    }
    
    /**
     * Inicializa la <code>PlantillaCombo</code> Individual de los <code>Componente</code>
     * incluidos con la <code>PlantillaCombo</code> de la Distribución. 
     * @param objPlantillaComboDistribucionIndividualSeleccionada 
     */
    public void actualizarPlantillaComboSeleccionada(PlantillaCombo objPlantillaComboDistribucionIndividualSeleccionada){
       try{
        for(Componente objComponente : this.objComponentesDistribuidos){
            if(!objComponente.getObjPlantillaComboDistribucionIndividualSeleccionada().getStrTitulo().equals(objPlantillaComboDistribucionIndividualSeleccionada.getStrTitulo())){
                objComponente.setObjPlantillaComboDistribucionIndividualSeleccionada(objPlantillaComboDistribucionIndividualSeleccionada);
            }
        }
        }catch(java.lang.NullPointerException ex){}
       
    }
    
    /**
     * Busca el <code>Componente</code> en la Colección de <code>Componente</code>
     * Incluidos que corresponde al Nombre recibido en el Parametro.
     * @param strName Nombre del <code>Componente</code> que se buscará en la Colección de <code>Componente</code>
     * Incluidos.
     * @return Referencia del <code>Componente</code> en caso de encontrarse o <code>null</code>.
     */
    public Componente buscarComponenteIncluidoEnDistribucion(String strName) {        
       try{
        try{
            for(Componente objComponenteIncluido : this.objComponentesDistribuidos){
                String strPropertiesName = objComponenteIncluido.getName();
                if(strName.equals(strPropertiesName)){
                    return objComponenteIncluido;                    
                }                
            }                                         
        }catch(java.lang.NullPointerException ex){
            Logger.getLogger(Distribuidor.class.getName()).log(Level.SEVERE, null, ex);
        }
       }catch(java.lang.NullPointerException ex){}
        return null;
    }

    @Override
    public Object clone() {        
       try{
        //----------------------------------------------------------
        //Primero se Clona la Referencia Local
        //----------------------------------------------------------
        Distribuidor objClon = (Distribuidor) super.clone();
        //----------------------------------------------------------
        //Ahora se hace la Clonación Profunda
        //----------------------------------------------------------        
        if(this.objComponentesDistribuidos != null){
            ((Distribuidor)objClon).objComponentesDistribuidos = deepCloningComponente();
        }
        if(this.objDataTablaComponente != null){
            ((Distribuidor)objClon).objDataTablaComponente = objDataTablaComponente.clone();
        }
        if(this.objDataTablaDistribucion != null){
            ((Distribuidor)objClon).objDataTablaDistribucion = objDataTablaDistribucion.clone();
        }
        if(this.objPlantillaCombo != null){
            ((Distribuidor)objClon).objPlantillaCombo = (PlantillaCombo) objPlantillaCombo.clone();
        }
        if(this.objTituloTablaComponente != null){
            ((Distribuidor)objClon).objTituloTablaComponente = objTituloTablaComponente.clone();
        }
        if(this.objTituloTablaDistribucion != null){
            ((Distribuidor)objClon).objTituloTablaDistribucion = objTituloTablaDistribucion.clone();
        }
        return objClon;
    }catch(java.lang.NullPointerException ex){}
       return null;
    }
    
    private ArrayList<Componente> deepCloningComponente(){
        ArrayList<Componente> objArrayCopia = new ArrayList<Componente>();
        for(Componente objComponente : objArrayCopia){
            if(objComponente instanceof ComponenteGeneradoSimple){
                objArrayCopia.add((ComponenteGeneradoSimple) objComponente.clone());
            }
        }
        return objArrayCopia;
    }

    @Override
    public String toString() {
       try{
        if(objPlantillaCombo != null){
            return objPlantillaCombo.toString();
        }
        return "Indefinido";
    }catch(java.lang.NullPointerException ex){}
       return "";
    }
    
    
}